package cn.wgx.commons.storage.fastdfs;


import cn.wgx.common.util.SpringUtils;
import cn.wgx.common.util.StringUtils;
import cn.wgx.commons.storage.fastdfs.conn.FdfsWebServer;
import cn.wgx.commons.storage.fastdfs.domain.StorePath;
import cn.wgx.commons.storage.fastdfs.exception.FdfsServerException;
import cn.wgx.commons.storage.fastdfs.exception.FdfsUnsupportStorePathException;
import cn.wgx.commons.storage.fastdfs.proto.storage.DownloadByteArray;
import cn.wgx.commons.storage.fastdfs.proto.storage.DownloadInputStream;
import cn.wgx.commons.storage.fastdfs.service.DefaultAppendFileStorageClient;
import cn.wgx.commons.storage.fastdfs.service.DefaultFastFileStorageClient;
import com.alibaba.fastjson.JSONArray;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit;

public class FastDFSClient {

    private static final Logger logger = LoggerFactory.getLogger(FastDFSClient.class);

    private static DefaultFastFileStorageClient storageClient;

    //断点续传
    private static DefaultAppendFileStorageClient defaultAppendFileStorageClient;

    private static FdfsWebServer fdfsWebServer;

    private static RedisTemplate redisTemplate;

    static {
        storageClient = SpringUtils.getBean("defaultFastFileStorageClient");

        fdfsWebServer = SpringUtils.getBean("fdfsWebServer");

        defaultAppendFileStorageClient = SpringUtils.getBean("defaultAppendFileStorageClient");

        redisTemplate = SpringUtils.getBean("redisTemplate");
    }

    /**
     * 获取fastdfs根目录
     *
     * @return
     */
    public static String getFdfsHome() {
        return fdfsWebServer.getFileHome();
    }

    /**
     * 上传文件
     *
     * @param file web文件对象
     * @return 文件访问地址
     * @throws IOException
     */
    public static String uploadFile(MultipartFile file) throws IOException {
        StorePath storePath = storageClient.uploadFile(file.getInputStream(), file.getSize(), FilenameUtils.getExtension(file.getOriginalFilename()), null);
        return storePath.getFullPath();
    }

    /**
     * 上传文件 - 可续传文件
     *
     * @param file
     * @param md5
     * @param allSize  文件总大小
     * @param offSet   偏移量
     * @param fileName 文件原始名
     * @return
     * @throws IOException
     */
    public static StorePath uploadFileAppend(MultipartFile file, String md5, long allSize, long offSet, String fileName) throws IOException {
        StorePath storePath = null;
        Object o = redisTemplate.opsForValue().get("appendFile_" + md5);
        //判断如果当前上传的文件不是上次的续传则拒绝
        if (o != null) {
            storePath = (StorePath) o;
            if ((storePath.getSuccessOffset()) != offSet) {
                return storePath;
            }
        }
        //当前上传块大小
        long nowSize = file.getSize();
        //第一次传新建
        if (offSet == 0) {
            storePath = defaultAppendFileStorageClient.uploadAppenderFile(file.getInputStream(), file.getSize(), FilenameUtils.getExtension(fileName));
            storePath.setSize(allSize);
        } else {//第二次以上就续传增加文件
            if (o == null) {
                return null;
            }
            storePath = (StorePath) o;
            defaultAppendFileStorageClient.modifyFile(storePath.getGroup(), storePath.getPath(), file.getInputStream(), file.getSize(), offSet);
        }
        storePath.setSuccessOffset(offSet + nowSize);
        //将当前上传进度存至redis
        redisTemplate.opsForValue().set("appendFile_" + md5, storePath, 48, TimeUnit.HOURS);
        //如果传送偏移量加文件块大小大于总文件大小表示本文件传送完毕
        if ((offSet + nowSize) >= allSize) {
            redisTemplate.delete("appendFile_" + md5);
        }
        if (logger.isDebugEnabled()) {
            logger.debug("fastdfs分片上传,偏移量{},共{}字节.", offSet + 1, allSize);
        }
        return storePath;
    }

    /**
     * 上传文件
     *
     * @param bytes 字节数组
     * @return 文件访问地址
     * @throws IOException
     */
    public static String uploadFile(byte[] bytes, String ext) {
        StorePath storePath = storageClient.uploadFile(new ByteArrayInputStream(bytes), new Long(bytes.length), ext, null);
        return storePath.getFullPath();
    }

    /**
     * 上传文件
     *
     * @param file 文件对象
     * @return 文件访问地址
     * @throws IOException
     */
    public static String uploadFile(File file) throws IOException {
        FileInputStream inputStream = new FileInputStream(file);
        StorePath storePath = storageClient.uploadFile(inputStream, file.length(), FilenameUtils.getExtension(file.getName()), null);
        if (inputStream != null) {
            inputStream.close();
        }
        return storePath.getFullPath();
    }

    /**
     * 上传文件
     *
     * @param file 文件对象
     * @return 文件访问地址
     * @throws IOException
     */
    public static String uploadAppendFile(File file) throws IOException {
        FileInputStream inputStream = new FileInputStream(file);
        StorePath storePath = storageClient.uploadFile(inputStream, file.length(), FilenameUtils.getExtension(file.getName()), null);
        if (inputStream != null) {
            inputStream.close();
        }
        return storePath.getFullPath();
    }

    /**
     * 将一段字符串生成一个文件上传
     *
     * @param content       文件内容
     * @param fileExtension
     * @return
     */
    public static String uploadFile(String content, String fileExtension) throws IOException {
        byte[] buff = content.getBytes(Charset.forName("UTF-8"));
        ByteArrayInputStream stream = new ByteArrayInputStream(buff);
        StorePath storePath = storageClient.uploadFile(stream, buff.length, fileExtension, null);
        if (stream != null) {
            stream.close();
        }
        return storePath.getFullPath();
    }

    // 封装图片完整URL地址
    private static String getResAccessUrl(StorePath storePath) {
        String fileUrl = fdfsWebServer.getWebServerUrl() + storePath.getFullPath();
        return fileUrl;
    }

    public static String getFileWebServerUrl() {
        return fdfsWebServer.getWebServerUrl();
    }

    /**
     * 删除文件
     *
     * @param fileUrl 文件访问地址
     * @return
     */
    public static void deleteFile(String fileUrl) {
        if (StringUtils.isEmpty(fileUrl)) {
            return;
        }
        try {
            StorePath storePath = StorePath.praseFromUrl(fileUrl);
            storageClient.deleteFile(storePath.getGroup(), storePath.getPath());
        } catch (FdfsServerException e) {
            logger.warn("删除失败: " + fileUrl + ". error:" + e.getMessage());
        } catch (FdfsUnsupportStorePathException e) {
            logger.warn(e.getMessage());
        } catch (Exception e) {
            logger.warn(e.getMessage());
        }
    }

    public static void deleteFile(JSONArray jsonArray) {
        if (jsonArray != null) {
            int n = jsonArray.size();
            for (int i = 0; i < n; i++) {
                if (jsonArray.get(i) != null) {
                    deleteFile(jsonArray.get(i).toString());
                }
            }
        }
    }

    /**
     * 下载文件
     *
     * @param fileUrl 文件URL
     * @return 文件字节
     * @throws IOException
     */
    public static byte[] downloadFile(String fileUrl) throws IOException {
        String group = fileUrl.substring(0, fileUrl.indexOf("/"));
        String path = fileUrl.substring(fileUrl.indexOf("/") + 1);
        DownloadByteArray downloadByteArray = new DownloadByteArray();
        byte[] bytes = storageClient.downloadFile(group, path, downloadByteArray);
        return bytes;
    }

    /**
     * 下载文件
     *
     * @param fileUrl 文件URL
     * @return 文件字节
     * @throws IOException
     */
    public static Long downloadFileByOutpuStream(String fileUrl, OutputStream outputStream) {
        String group = fileUrl.substring(0, fileUrl.indexOf("/"));
        String path = fileUrl.substring(fileUrl.indexOf("/") + 1);
        DownloadInputStream downloadByteArray = new DownloadInputStream(outputStream);
        Long l = storageClient.downloadFileGetInputstream(group, path, downloadByteArray);
        return l;
    }

}
